//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/* This is an intial version of the ViRGE driver for XAA
 * Started 09/03/97 by S. Marineau
 *
 * What works:
 * - Supports PCI hardware, ViRGE and ViRGE/VX, probably ViRGE/DXGX
 * - Supports 8bpp, 16bpp and 24bpp. There is some support for 32bpp.
 * - VT switching seems to work well, no corruption.
 * - A whole slew of XConfig options for memory, PCI and acceleration
 * - Acceleration is quite complete
 *
 *
 * What does not work:
 * - None of this doublescan stuff
 * - No hardware cursor etc.
 * - No VLB
 *
 *
 * What I attempt to do here:
 *
 *  - Rewrite the init/save functions from the accel server such that they
 *    work as XAA intends
 *  - Keep the driver variables inside a private data structure, to avoid having
 *    millions of global variables.
 *  - Keep the structure as simple as possible, try to move RAMDAC stuff to
 *    separate source files.
 *
 *  So much for that.... We cannot use the s3 ramdac code, because we
 *  want to wait before we write out any registers. Fortunately, the
 *  built-in virge ramdac is straighforward to set-up. Also, I did not succeed
 *  in keeping the code as modular as I had wished...
 *
 *
 * Notes:
 * - The driver only supports linear addressing and MMIO.
 *
 */
#include <ddk.h>
#define USE_XAA
#include "X.h"

#include "compiler.h"

#include "xf86.h"
#include "xf86Priv.h"
#include "xf86_OSlib.h"
#include "xf86_HWlib.h"
#define XCONFIG_FLAGS_ONLY
#include "xf86_Config.h"
#include "gdivga.h"
#include "xf86_pci.h"
#include "xf86procs.h"

#include "vgamisc.h"

/* S3V internal includes */
#include "s3v_driver.h"
#include "regs3v.h"

static Boolean    S3VProbe();
static char *  S3VIdent(int);
//static Boolean    S3VClockSelect();
static void    S3VEnterLeave(Boolean);
static Boolean    S3VInit(DisplayModePtr mode);
static int     S3VValidMode(DisplayModePtr mode, Boolean verbose, int flag);
static void *  S3VSave(void * lpSave);
static void    S3VRestore(void *lpRes);
static void    S3VAdjust(int x, int y);
static void    S3VFbInit();
void           S3VSetRead();
void           S3VAccelInit();
void           S3VAccelInit32();
void           S3VInitSTREAMS(int * streams, DisplayModePtr mode);
void           S3VDisableSTREAMS();
void           S3VRestoreSTREAMS(int *streams);
void           S3VSaveSTREAMS(int *streams);

/* Temporary debug function to print virge regs */
void S3VPrintRegs();

/*
 * And the data structure which defines the driver itself
 * This is mostly the struct from the s3 driver with adjusted names
 * and adjusted default values.
 */

vgaVideoChipRec S3V = {
  S3VProbe,              /* Boolean (* ChipProbe)() */
  S3VIdent,              /* char * (* ChipIdent)() */
  S3VEnterLeave,         /* void (* ChipEnterLeave)() */
  S3VInit,               /* Boolean (* ChipInit)() */
  S3VValidMode,          /* int (* ChipValidMode)() */
  S3VSave,               /* void * (* ChipSave)() */
  S3VRestore,            /* void (* ChipRestore)() */
  S3VAdjust,             /* void (* ChipAdjust)() */
  vgaHWSaveScreen,       /* void (* ChipSaveScreen)() */
  (void(*)())NoopDDA,    /* void (* ChipGetMode)() */
  S3VFbInit,             /* void (* ChipFbInit)() */
  (void (*)())NoopDDA,   /* void (* ChipSetRead)() */
  (void (*)())NoopDDA,   /* void (* ChipSetWrite)() */
  (void (*)())NoopDDA,   /* void (* ChipSetReadWrite)() */
  0x10000,              /* int ChipMapSize */
  0x10000,              /* int ChipSegmentSize */
  16,                   /* int ChipSegmentShift */
  0xFFFF,               /* int ChipSegmentMask */
  0x00000, 0x10000,      /* int ChipReadBottom, int ChipReadTop */
  0x00000, 0x10000,     /* int ChipWriteBottom, int ChipWriteTop */
  FALSE,                /* Boolean ChipUse2Banks */
  VGA_DIVIDE_VERT,      /* int ChipInterlaceType */
  {0,},                 /* OFlagSet ChipOptionFlags */
  8,                    /* int ChipRounding */
  TRUE,                 /* Boolean ChipUseLinearAddressing */
  0,                    /* int ChipLinearBase */
  0,                    /* int ChipLinearSize */
  /*
   * This is TRUE if the driver has support for the given depth for
   * the detected configuration. It must be set in the Probe function.
   * It most cases it should be FALSE.
   */
  TRUE,        /* 16bpp */
  TRUE,        /* 24bpp */
  TRUE,        /* 32bpp */
  NULL,                 /* DisplayModePtr ChipBuiltinModes */
    /*
     * This is a factor that can be used to scale the raw clocks
     * to pixel clocks.     This is rarely used, and in most cases, set
     * it to 1.
     */
 1
};

/* entries must be in sequence with chipset numbers !! */
SymTabRec s3vChipTable[] = {
   { S3_UNKNOWN,   "unknown"},
   { S3_ViRGE,     "ViRGE"},
   { S3_ViRGE_VX,  "ViRGE/VX"},
   { S3_ViRGE_DXGX,"ViRGE/DXGX"},
   { S3_ViRGE_GX2, "ViRGE/GX2"},
   { S3_ViRGE_MX,  "ViRGE/MX"},
   { S3_ViRGE_MXP, "ViRGE/MX+"},
   { S3_TRIO_3D,   "Trio3D"},
   { S3_TRIO_3D_2X,"Trio3D/2X"},
   { S3_SAVAGE_3D, "Savage3D"},
   { S3_SAVAGE_3D_M, "Savage3D (Macrovision Support)"},
   { -1,           ""},
};

/* Declare the private structure which stores all internal info */

S3VPRIV s3vPriv;


/* And other glabal vars to hold vga base regs and MMIO base mem pointer */

//int vgaCRIndex, vgaCRReg;
pointer s3vMmioMem = NULL;   /* MMIO base address */
extern int vgaBitsPerPixel;

/* This function returns the string name for a supported chipset */

static char *
S3VIdent(int n)
{
    char *chipset = "s3_virge";

    if(n == 0)
        return(chipset);
    else
        return NULL;
}

/* SetMMIO() - FALSE: restore MMIO settings
               TRUE : enable  MMIO
*/
static void
S3VSetMMIO(Boolean mmio_enable)
{
    static unsigned char cr40, cr53;
    static Boolean in_mmio=FALSE;

    if(mmio_enable) { /* Enable MMIO */
        if(!in_mmio) {
            Outb(vgaCRIndex, 0x53);
            cr53 = Inb(vgaCRReg);
            Outb(vgaCRReg, (cr53 & 0xef) | 0x08);
            Outb(vgaCRIndex, 0x40);
            cr40 = Inb(vgaCRReg);

            if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
                Outb(vgaCRReg, cr40 | 0x01);
            }
            else {
                Outb(vgaCRReg, cr40 & ~0x01);
            }
            in_mmio=TRUE;
        }
    }
    else {          /* Restore MMIO settings */
        if(in_mmio) {
            Outb(vgaCRIndex, 0x40);
            Outb(vgaCRReg, cr40);
            Outb(vgaCRIndex, 0x53);
            Outb(vgaCRReg, cr53);   /* Restore CR53 to original for MMIO */
            in_mmio=FALSE;
        }
    }
}

/* The EnterLeave function which en/dis access to IO ports and ext. regs */

static void
S3VEnterLeave(Boolean enter)
{
    static int enterCalled = FALSE;
    unsigned char tmp;

#ifdef XFreeXDGA
    if (SvgaInfoRec.directMode&XF86DGADirectGraphics && !enter) {
        if (vgaHWCursor.Initialized)
            S3VHideCursor();
        return;
    }
#endif

    if (enter){
        xf86ClearIOPortList(SvgaInfoRec.scrnIndex);
        xf86AddIOPorts(SvgaInfoRec.scrnIndex, Num_VGA_IOPorts, VGA_IOPorts);
        xf86EnableIOPorts(SvgaInfoRec.scrnIndex);

        /* Init the vgaIOBase reg index, depends on mono/color operation */
        vgaIOBase = (Inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
        vgaCRIndex = vgaIOBase + 4;
        vgaCRReg = vgaIOBase + 5;

        /* Unprotect CRTC[0-7]  */
        Outb(vgaCRIndex, 0x11);      /* for register CR11 */
        tmp = Inb(vgaCRReg);         /* enable CR0-7 and disable interrupts */
        Outb(vgaCRReg, tmp & 0x7f);

        /* And unlock extended regs */
        Outb(vgaCRIndex, 0x38);      /* for register CR38, (REG_LOCK1) */
        Outb(vgaCRReg, 0x48);        /* unlock S3 register set for read/write */
        Outb(vgaCRIndex, 0x39);
        Outb(vgaCRReg, 0xa5);

        if (S3_TRIO_3D_SERIES(s3vPriv.chip)) {
            S3VSetMMIO(TRUE); /* Enable MMIO all the time we're in graphics */
        }
        else {
            Outb(vgaCRIndex, 0x40);
            tmp = Inb(vgaCRReg);
            Outb(vgaCRReg, tmp & ~0x01);   /* avoid lockups when reading I/O port 0x92e8 */
        }
        enterCalled = TRUE;
    }
    else {
        if (s3vMmioMem) {
            unsigned char cr3a, cr53 = 0, cr66;
            if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
                Outb(vgaCRIndex, 0x53);
                cr53 = Inb(vgaCRReg);
                Outb(vgaCRReg, cr53 | 0x08);  /* Enable NEWMMIO temporarily */
            }

            Outb(vgaCRIndex, 0x66);
            cr66 = Inb(vgaCRReg);
            Outb(vgaCRReg, cr66 | 0x80);
            Outb(vgaCRIndex, 0x3a);
            cr3a = Inb(vgaCRReg);
            Outb(vgaCRReg, cr3a | 0x80);

            WaitIdle();           /* DOn't know if these map properly ? */
            WaitCommandEmpty();   /* We should probably do a DMAEmpty() as well */

            if (S3_TRIO_3D_SERIES(s3vPriv.chip)) {
                S3VSetMMIO(FALSE);
            } else {
                Outb(vgaCRIndex, 0x53);
                Outb(vgaCRReg, cr53);   /* Restore CR53 to original for MMIO */
            }

            Outb(vgaCRIndex, 0x66);
            Outb(vgaCRReg, cr66);
            Outb(vgaCRIndex, 0x3a);
            Outb(vgaCRReg, cr3a);
        }

        if (enterCalled) {

            /* Protect CR[0-7] */
            Outb(vgaCRIndex, 0x11);      /* for register CR11 */
            tmp = Inb(vgaCRReg);         /* disable CR0-7 */
            Outb(vgaCRReg, (tmp & 0x7f) | 0x80);

            /* Relock extended regs-> To DO */

            xf86DisableIOPorts(SvgaInfoRec.scrnIndex);
            enterCalled = FALSE;
        }
    }
}


void
S3VGEReset(int from_timeout, int line, char *file)
{
}
/*
 * This function is used to restore a video mode. It writes out all
 * of the standart VGA and extended S3 registers needed to setup a
 * video mode.
 *
 * Note that our life is made more difficult because of the STREAMS
 * processor which must be used for 24bpp. We need to disable STREAMS
 * before we switch video modes, or we risk locking up the machine.
 * We also have to follow a certain order when reenabling it.
 */

static void
S3VRestore(void *lpRes)
{
    unsigned char tmp, cr3a, cr53 = 0, cr66, cr67;
    vgaS3VPtr restore = (vgaS3VPtr)lpRes;

    vgaProtect(TRUE);

    /* Are we going to reenable STREAMS in this new mode? */
    s3vPriv.STREAMSRunning = restore->CR67 & 0x0c;

    /* first enable NEWMMIO, it's needed in S3VGEReset() */
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        Outb(vgaCRReg, restore->CR53 | 0x08);
    }

    /* Then reset GE to make sure nothing is going on */
    if(s3vPriv.chip == S3_ViRGE_VX) {
        Outb(vgaCRIndex, 0x63);
        if(Inb(vgaCRReg) & 0x01) S3VGEReset(0,__LINE__,__FILE__);
    }
    else {
        Outb(vgaCRIndex, 0x66);
        if(Inb(vgaCRReg) & 0x01) S3VGEReset(0,__LINE__,__FILE__);
    }

    /* As per databook, always disable STREAMS before changing modes */
    Outb(vgaCRIndex, 0x67);
    cr67 = Inb(vgaCRReg);
    if ((cr67 & 0x0c) == 0x0c) {
        S3VDisableSTREAMS();     /* If STREAMS was running, disable it */
    }

    /* Restore S3 extended regs */
    if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        VerticalRetraceWait();        /* As suggested by [trio3d] page 73  */
    }
    else {
        Outb(vgaCRIndex, 0x63);
        Outb(vgaCRReg, restore->CR63);
    }
    Outb(vgaCRIndex, 0x66);
    Outb(vgaCRReg, restore->CR66);
    Outb(vgaCRIndex, 0x3a);
    Outb(vgaCRReg, restore->CR3A);
    Outb(vgaCRIndex, 0x31);
    Outb(vgaCRReg, restore->CR31);
    Outb(vgaCRIndex, 0x58);
    Outb(vgaCRReg, restore->CR58);
    Outb(vgaCRIndex, 0x55);
    Outb(vgaCRReg, restore->CR55);

    Outb(0x3c4, 0x08);
    Outb(0x3c5, 0x06);
    Outb(0x3c4, 0x12);
    Outb(0x3c5, restore->SR12);
    Outb(0x3c4, 0x13);
    Outb(0x3c5, restore->SR13);
    if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Outb(0x3c4, 0x29);
        Outb(0x3c5, restore->SR29);
        Outb(0x3c4, 0x54);
        Outb(0x3c5, restore->SR54);
        Outb(0x3c4, 0x55);
        Outb(0x3c5, restore->SR55);
        Outb(0x3c4, 0x56);
        Outb(0x3c5, restore->SR56);
        Outb(0x3c4, 0x57);
        Outb(0x3c5, restore->SR57);
    }
    Outb(0x3c4, 0x15);
    Outb(0x3c5, restore->SR15);
    if (S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(0x3c4, 0x0A);
        Outb(0x3c5, restore->SR0A);
        Outb(0x3c4, 0x0F);
        Outb(0x3c5, restore->SR0F);
    }

    /* Restore the standard VGA registers */
    vgaHWRestore((vgaHWPtr)restore);

    /* Extended mode timings registers */
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        Outb(vgaCRReg, restore->CR53);
    }
    Outb(vgaCRIndex, 0x5d);
    Outb(vgaCRReg, restore->CR5D);
    Outb(vgaCRIndex, 0x5e);
    Outb(vgaCRReg, restore->CR5E);
    Outb(vgaCRIndex, 0x3b);
    Outb(vgaCRReg, restore->CR3B);
    Outb(vgaCRIndex, 0x3c);
    Outb(vgaCRReg, restore->CR3C);
    Outb(vgaCRIndex, 0x43);
    Outb(vgaCRReg, restore->CR43);
    Outb(vgaCRIndex, 0x65);
    Outb(vgaCRReg, restore->CR65);
    Outb(vgaCRIndex, 0x6d);
    Outb(vgaCRReg, restore->CR6D);


    /* Restore the desired video mode with CR67 */

    Outb(vgaCRIndex, 0x67);
    cr67 = Inb(vgaCRReg) & 0xf; /* Possible hardware bug on VX? */
    Outb(vgaCRReg, 0x50 | cr67);

    //_CThread_Delay(10000);
    DzDelay(1000);
    Outb(vgaCRIndex, 0x67);
    Outb(vgaCRReg, restore->CR67 & ~0x0c); /* Don't enable STREAMS yet */

    /* Other mode timing and extended regs */
    Outb(vgaCRIndex, 0x34);
    Outb(vgaCRReg, restore->CR34);
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x40);
        Outb(vgaCRReg, restore->CR40);
    }
    if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x41);
        Outb(vgaCRReg, restore->CR41);
    }
    Outb(vgaCRIndex, 0x42);
    Outb(vgaCRReg, restore->CR42);
    Outb(vgaCRIndex, 0x45);
    Outb(vgaCRReg, restore->CR45);
    Outb(vgaCRIndex, 0x51);
    Outb(vgaCRReg, restore->CR51);
    Outb(vgaCRIndex, 0x54);
    Outb(vgaCRReg, restore->CR54);

    /* Memory timings */
    Outb(vgaCRIndex, 0x36);
    Outb(vgaCRReg, restore->CR36);
    Outb(vgaCRIndex, 0x68);
    Outb(vgaCRReg, restore->CR68);
    Outb(vgaCRIndex, 0x69);
    Outb(vgaCRReg, restore->CR69);

    Outb(vgaCRIndex, 0x33);
    Outb(vgaCRReg, restore->CR33);
    if (s3vPriv.chip == S3_ViRGE_DXGX) {
        Outb(vgaCRIndex, 0x86);
        Outb(vgaCRReg, restore->CR86);
    }
    if (s3vPriv.chip == S3_ViRGE_DXGX || S3_ViRGE_GX2_SERIES(s3vPriv.chip) ||
        S3_ViRGE_MX_SERIES(s3vPriv.chip) || S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x90);
        Outb(vgaCRReg, restore->CR90);
        Outb(vgaCRIndex, 0x91);
        Outb(vgaCRReg, restore->CR91);
    }

    /* Unlock extended sequencer regs */
    Outb(0x3c4, 0x08);
    Outb(0x3c5, 0x06);


    /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates that
    * we should leave the default SR10 and SR11 values there.
    */

    if (restore->SR10 != 255) {
        Outb(0x3c4, 0x10);
        Outb(0x3c5, restore->SR10);
        Outb(0x3c4, 0x11);
        Outb(0x3c5, restore->SR11);
    }

    /* Restore extended sequencer regs for DCLK */
    Outb(0x3c4, 0x12);
    Outb(0x3c5, restore->SR12);
    Outb(0x3c4, 0x13);
    Outb(0x3c5, restore->SR13);
    if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Outb(0x3c4, 0x29);
        Outb(0x3c5, restore->SR29);
        Outb(0x3c4, 0x54);
        Outb(0x3c5, restore->SR54);
        Outb(0x3c4, 0x55);
        Outb(0x3c5, restore->SR55);
        Outb(0x3c4, 0x56);
        Outb(0x3c5, restore->SR56);
        Outb(0x3c4, 0x57);
        Outb(0x3c5, restore->SR57);
    }

    Outb(0x3c4, 0x18);
    Outb(0x3c5, restore->SR18);

    /* Load new m,n PLL values for DCLK & MCLK */
    Outb(0x3c4, 0x15);
    tmp = Inb(0x3c5) & ~0x21;

    Outb(0x3c5, tmp | 0x03);
    Outb(0x3c5, tmp | 0x23);
    Outb(0x3c5, tmp | 0x03);
    Outb(0x3c5, restore->SR15);

    Outb(0x3c4, 0x08);
    Outb(0x3c5, restore->SR8);


   /* Now write out CR67 in full, possibly starting STREAMS */


    VerticalRetraceWait();
    Outb(vgaCRIndex, 0x67);
    Outb(vgaCRReg, 0x50);   /* For possible bug on VX?! */

    //_CThread_Delay(10000);
    DzDelay(1000);
    Outb(vgaCRIndex, 0x67);
    Outb(vgaCRReg, restore->CR67);


   /* And finally, we init the STREAMS processor if we have CR67 indicate 24bpp
    * We also restore FIFO and TIMEOUT memory controller registers.
    */

    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        cr53 = Inb(vgaCRReg);
        Outb(vgaCRReg, cr53 | 0x08);  /* Enable NEWMMIO temporarily */
    }

    Outb(vgaCRIndex, 0x66);
    cr66 = Inb(vgaCRReg);
    Outb(vgaCRReg, cr66 | 0x80);
    Outb(vgaCRIndex, 0x3a);
    cr3a = Inb(vgaCRReg);
    Outb(vgaCRReg, cr3a | 0x80);

    if (s3vPriv.NeedSTREAMS) {
        if(s3vPriv.STREAMSRunning) S3VRestoreSTREAMS((int*)restore->STREAMS);
    }

   /* Now, before we continue, check if this mode has the graphic engine ON
    * If yes, then we reset it.
    * This fixes some problems with corruption at 24bpp with STREAMS
    * Also restore the MIU registers.
    */


    if(s3vPriv.chip == S3_ViRGE_VX) {
        if(restore->CR63 & 0x01) S3VGEReset(0,__LINE__,__FILE__);
    }
    else {
        if(restore->CR66 & 0x01) S3VGEReset(0,__LINE__,__FILE__);
    }

    VerticalRetraceWait();

    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        ((mmtr)s3vMmioMem)->memport_regs.regs.fifo_control = restore->MMPR0;

        /* for virge/DXGX the next line must be canceled, dengkang */
        //WaitIdle();                  /* Don't ask... */
        //((mmtr)s3vMmioMem)->memport_regs.regs.miu_control = restore->MMPR1;
        WaitIdle();
        ((mmtr)s3vMmioMem)->memport_regs.regs.streams_timeout = restore->MMPR2;

        /* for virge/DXGX the next line must be canceled , dengkang*/
        //WaitIdle();
        //((mmtr)s3vMmioMem)->memport_regs.regs.misc_timeout = restore->MMPR3;

        Outb(vgaCRIndex, 0x53);
        Outb(vgaCRReg, cr53);   /* Restore CR53 to original for MMIO */
    }

    Outb(vgaCRIndex, 0x66);
    Outb(vgaCRReg, cr66);
    Outb(vgaCRIndex, 0x3a);
    Outb(vgaCRReg, cr3a);

    if (xf86Verbose > 1) {
        kprintf("\n\nViRGE driver: done restoring mode,dumping CR registers:\n\n");
        S3VPrintRegs();
    }

    vgaProtect(FALSE);

}

/*
 * This function performs the inverse of the restore function: It saves all
 * the standard and extended registers that we are going to modify to set
 * up a video mode. Again, we also save the STREAMS context if it is needed.
 */

static void *
S3VSave(void * lpSave)
{
//    int i;
    unsigned char cr3a, cr53 = 0, cr66;
    vgaS3VPtr save = (vgaS3VPtr)lpSave;

   /*
    * This function will handle creating the data structure and filling
    * in the generic VGA portion.
    */

    Outb(vgaCRIndex, 0x66);
    cr66 = Inb(vgaCRReg);
    Outb(vgaCRReg, cr66 | 0x80);
    Outb(vgaCRIndex, 0x3a);
    cr3a = Inb(vgaCRReg);
    Outb(vgaCRReg, cr3a | 0x80);

    Outb(vgaCRIndex, 0x66);
    cr66 = Inb(vgaCRReg);
    Outb(vgaCRReg, cr66 | 0x80);
    Outb(vgaCRIndex, 0x3a);
    cr3a = Inb(vgaCRReg);
    Outb(vgaCRReg, cr3a | 0x80);

    save = (vgaS3VPtr)vgaHWSave((vgaHWPtr)save, sizeof(vgaS3VRec));

    Outb(vgaCRIndex, 0x66);
    Outb(vgaCRReg, cr66);
    Outb(vgaCRIndex, 0x3a);
    Outb(vgaCRReg, cr3a);

    Outb(vgaCRIndex, 0x66);
    Outb(vgaCRReg, cr66);
    Outb(vgaCRIndex, 0x3a);
    Outb(vgaCRReg, cr3a);

   /* First unlock extended sequencer regs */
    Outb(0x3c4, 0x08);
    save->SR8 = Inb(0x3c5);
    Outb(0x3c5, 0x06);

    /* Now we save all the s3 extended regs we need */
    Outb(vgaCRIndex, 0x31);
    save->CR31 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x34);
    save->CR34 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x36);
    save->CR36 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x3a);
    save->CR3A = Inb(vgaCRReg);
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x40);
        save->CR40 = Inb(vgaCRReg);
    }
    if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x41);
        save->CR41 = Inb(vgaCRReg);
    }
    Outb(vgaCRIndex, 0x42);
    save->CR42 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x45);
    save->CR45 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x51);
    save->CR51 = Inb(vgaCRReg);
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        save->CR53 = Inb(vgaCRReg);
    }
    Outb(vgaCRIndex, 0x54);
    save->CR54 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x55);
    save->CR55 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x58);
    save->CR58 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x63);
    save->CR63 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x66);
    save->CR66 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x67);
    save->CR67 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x68);
    save->CR68 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x69);
    save->CR69 = Inb(vgaCRReg);

    Outb(vgaCRIndex, 0x33);
    save->CR33 = Inb(vgaCRReg);
    if (s3vPriv.chip == S3_ViRGE_DXGX) {
        Outb(vgaCRIndex, 0x86);
        save->CR86 = Inb(vgaCRReg);
    }
    if (s3vPriv.chip == S3_ViRGE_DXGX || S3_ViRGE_GX2_SERIES(s3vPriv.chip) ||
        S3_ViRGE_MX_SERIES(s3vPriv.chip) || S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x90);
        save->CR90 = Inb(vgaCRReg);
        Outb(vgaCRIndex, 0x91);
        save->CR91 = Inb(vgaCRReg);
    }

   /* Extended mode timings regs */

    Outb(vgaCRIndex, 0x3b);
    save->CR3B = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x3c);
    save->CR3C = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x43);
    save->CR43 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x5d);
    save->CR5D = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x5e);
    save->CR5E = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x65);
    save->CR65 = Inb(vgaCRReg);
    Outb(vgaCRIndex, 0x6d);
    save->CR6D = Inb(vgaCRReg);

   /* Save sequencer extended regs for DCLK PLL programming */

    Outb(0x3c4, 0x10);
    save->SR10 = Inb(0x3c5);
    Outb(0x3c4, 0x11);
    save->SR11 = Inb(0x3c5);

    Outb(0x3c4, 0x12);
    save->SR12 = Inb(0x3c5);
    Outb(0x3c4, 0x13);
    save->SR13 = Inb(0x3c5);
    if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Outb(0x3c4, 0x29);
        save->SR29 = Inb(0x3c5);
        Outb(0x3c4, 0x54);
        save->SR54 = Inb(0x3c5);
        Outb(0x3c4, 0x55);
        save->SR55 = Inb(0x3c5);
        Outb(0x3c4, 0x56);
        save->SR56 = Inb(0x3c5);
        Outb(0x3c4, 0x57);
        save->SR57 = Inb(0x3c5);
    }

    Outb(0x3c4, 0x15);
    save->SR15 = Inb(0x3c5);
    if (S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(0x3c4, 0x0A);
        save->SR0A = Inb(0x3c5);
        Outb(0x3c4, 0x0F);
        save->SR0F = Inb(0x3c5);
    }
    Outb(0x3c4, 0x18);
    save->SR18 = Inb(0x3c5);


    /* And if streams is to be used, save that as well */
    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        cr53 = Inb(vgaCRReg);
        Outb(vgaCRReg, cr53 | 0x08);  /* Enable NEWMMIO to save MIU context */
    }
    Outb(vgaCRIndex, 0x66);
    cr66 = Inb(vgaCRReg);
    Outb(vgaCRReg, cr66 | 0x80);
    Outb(vgaCRIndex, 0x3a);
    cr3a = Inb(vgaCRReg);
    Outb(vgaCRReg, cr3a | 0x80);

    if(s3vPriv.NeedSTREAMS) {
        S3VSaveSTREAMS((int*)save->STREAMS);
    }

    if(!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        /* Now save Memory Interface Unit registers, enable MMIO for this */
        save->MMPR0 = ((mmtr)s3vMmioMem)->memport_regs.regs.fifo_control;
        save->MMPR1 = ((mmtr)s3vMmioMem)->memport_regs.regs.miu_control;
        save->MMPR2 = ((mmtr)s3vMmioMem)->memport_regs.regs.streams_timeout;
        save->MMPR3 = ((mmtr)s3vMmioMem)->memport_regs.regs.misc_timeout;

        if (xf86Verbose > 1) {
            /* Debug */
            kprintf("MMPR regs: %08x %08x %08x %08x\n", \
                ((mmtr)s3vMmioMem)->memport_regs.regs.fifo_control, \
                ((mmtr)s3vMmioMem)->memport_regs.regs.miu_control, \
                ((mmtr)s3vMmioMem)->memport_regs.regs.streams_timeout, \
                ((mmtr)s3vMmioMem)->memport_regs.regs.misc_timeout );

            kprintf("\n\nViRGE driver: saved current video mode. Register dump:\n\n");
        }
    }

    if(!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x53);
        Outb(vgaCRReg, cr53);   /* Restore CR53 to original for MMIO */
    }

    Outb(vgaCRIndex, 0x3a);
    Outb(vgaCRReg, cr3a);
    Outb(vgaCRIndex, 0x66);
    Outb(vgaCRReg, cr66);

    if (xf86Verbose > 1) S3VPrintRegs();

    return ((void *) save);
}



static unsigned char
*find_bios_string(int BIOSbase, char *match1, char *match2)
{
#define BIOS_BSIZE 1024
#define BIOS_BASE  0xc0000

    static unsigned char bios[BIOS_BSIZE];
    static int init=0;
    int i,j,l1,l2;

    if (!init) {
        init = 1;
        if (xf86ReadBIOS(BIOSbase, 0, bios, BIOS_BSIZE) != BIOS_BSIZE)
            return NULL;
        if ((bios[0] != 0x55) || (bios[1] != 0xaa))
            return NULL;
    }
    if (match1 == NULL)
      return NULL;

    l1 = strlen(match1);
    if (match2 != NULL)
        l2 = strlen(match2);
    else    /* for compiler-warnings */
        l2 = 0;

    for (i=0; i<BIOS_BSIZE-l1; i++) {
        if (bios[i] == match1[0] && !memcmp(&bios[i],match1,l1)) {
            if (match2 == NULL)  {
                return &bios[i+l1];
            }
            else {
                for(j=i+l1; (j<BIOS_BSIZE-l2) && bios[j]; j++) {
                    if (bios[j] == match2[0] && !memcmp(&bios[j],match2,l2)) {
                        return &bios[j+l2];
                    }
                }
            }
        }
    }

    return NULL;
}

/*
 * s3vGetPCIInfo -- probe for PCI information
 */

S3PCIInformation *
s3vGetPCIInfo()
{
    static S3PCIInformation info = {0, };
    pciConfigPtr pcrp = NULL;
    Boolean found = FALSE;
    int i = 0;


    if (!SvgaInfoRec.chipset) {
        pcrp = vgaPCIInfo[i];
        while (pcrp) {
            if ((pcrp->_vendor == PCI_S3_VENDOR_ID)) {
                int ChipId = pcrp->_device;
                if (SvgaInfoRec.chipID) {
                    kprintf("%s %s: S3 chipset override, using chip_id = 0x%04x instead of 0x%04x\n", \
                        XCONFIG_GIVEN, SvgaInfoRec.name, SvgaInfoRec.chipID, ChipId);
                    ChipId = SvgaInfoRec.chipID;
                }
                found = TRUE;

                switch (ChipId) {
                case PCI_ViRGE:
                    info.ChipType = S3_ViRGE;
                    break;
                case PCI_ViRGE_VX:
                    info.ChipType = S3_ViRGE_VX;
                    break;
                case PCI_ViRGE_DXGX:
                    info.ChipType = S3_ViRGE_DXGX;
                    break;
                case PCI_ViRGE_GX2:
                    info.ChipType = S3_ViRGE_GX2;
                    break;
                case PCI_ViRGE_MX:
                    info.ChipType = S3_ViRGE_MX;
                    break;
                case PCI_ViRGE_MXP:
                    info.ChipType = S3_ViRGE_MXP;
                    break;
                case PCI_TRIO_3D:
                    info.ChipType = S3_TRIO_3D;
                    break;
                case PCI_TRIO_3D_2X:
                    info.ChipType = S3_TRIO_3D_2X;
                    break;
#if 0 /* not yet */
                case PCI_CHIP_SAVAGE3D:
                    info.ChipType = S3_SAVAGE_3D;
                    break;
                case PCI_CHIP_SAVAGE3D_M:
                    info.ChipType = S3_SAVAGE_3D_M;
                    break;
#endif
                default:
                    info.ChipType = S3_UNKNOWN;
                    info.DevID = pcrp->_device;
	       break;
                }
                info.ChipRev = pcrp->_rev_id;
                info.MemBase = pcrp->_base0 & 0xFF800000;
                break;
            }
            i++;
            pcrp = vgaPCIInfo[i];
        }
    }
    else {
        return (FALSE);
    }

   /* for new mmio we have to ensure that the PCI base address is
    * 64MB aligned and that there are no address collitions within 64MB.
    * S3 868/968 only pretend to need 32MB and thus fool
    * the BIOS PCI auto configuration :-(
    */

    if (info.ChipType == S3_ViRGE) {
        unsigned long base0;
        char *probed;
        char map_64m[64];
        int j;

        if (SvgaInfoRec.MemBase == 0) {
            base0  = info.MemBase;
            probed = XCONFIG_PROBED;
        }
        else {
            base0  = SvgaInfoRec.MemBase;
            probed = XCONFIG_GIVEN;
        }

      /* map allocated 64MB blocks */
        for (j=0; j<64; j++) map_64m[j] = 0;
        map_64m[63] = 1;  /* don't use the last 64MB area */
        for (j=0; (pcrp = vgaPCIInfo[j]); j++) {
            if (i != j) {
                map_64m[ (pcrp->_base0 >> 26) & 0x3f] = 1;
                map_64m[ (pcrp->_base1 >> 26) & 0x3f] = 1;
                map_64m[ (pcrp->_base2 >> 26) & 0x3f] = 1;
                map_64m[ (pcrp->_base3 >> 26) & 0x3f] = 1;
                map_64m[ (pcrp->_base4 >> 26) & 0x3f] = 1;
                map_64m[ (pcrp->_base5 >> 26) & 0x3f] = 1;
            }
        }

      /* check for 64MB alignment and free space */
        if ((base0 & 0x3ffffff) ||
            map_64m[(base0 >> 26) & 0x3f] ||
            map_64m[((base0+0x3ffffff) >> 26) & 0x3f]) {
            for (j=63; j>=16 && map_64m[j]; j--);
            info.MemBase = ((unsigned long)j) << 26;
            kprintf("%s %s: S3V: PCI base address not correctly aligned or address conflict\n", \
                probed, SvgaInfoRec.name);
            kprintf("\t\tbase address changed from 0x%08lx to 0x%08lx\n", \
                base0, info.MemBase);
            xf86writepci(SvgaInfoRec.scrnIndex, vgaPCIInfo[i]->_bus,
                vgaPCIInfo[i]->_cardnum,
                vgaPCIInfo[i]->_func,
                PCI_MAP_REG_START, ~0L,
                info.MemBase | PCI_MAP_MEMORY | PCI_MAP_MEMORY_TYPE_32BIT);
        }
    }
    else {
        /* Don't do this check for other chipsets. */
    }

    if (found && xf86Verbose) {
        if (info.ChipType != S3_UNKNOWN) {
            kprintf("%s %s: S3V: %s rev %x, Linear FB @ 0x%08lx\n", XCONFIG_PROBED, \
                SvgaInfoRec.name,xf86TokenToString(s3vChipTable, info.ChipType), \
                info.ChipRev, info.MemBase);
        }
    }

    if (found)
        return &info;
    else
        return NULL;
}




/*
 * This is the main probe function for the virge chipsets.
 * Right now, I have taken a shortcut and get most of the info from
 * PCI probing. Some code will have to be added to support VLB cards.
 */

static Boolean
S3VProbe()
{
    S3PCIInformation *pciInfo = NULL;
    unsigned char config1, config2, m, n, n1, n2, cr66 = 0, sr8;
    int mclk;
    DisplayModePtr pMode, pEnd;

    if (SvgaInfoRec.chipset) {
        if (StrCaseCmp(SvgaInfoRec.chipset,S3VIdent(0)))
            return(FALSE);
    }

    /* Start with PCI probing, this should get us quite far already */
    /* For now, we only use the PCI probing; add in later VLB */

    pciInfo = s3vGetPCIInfo();
    if (!pciInfo)
        return FALSE;

    if (pciInfo && pciInfo->MemBase && !SvgaInfoRec.MemBase)
        SvgaInfoRec.MemBase = pciInfo->MemBase;
    if (pciInfo) {
        if(pciInfo->ChipType != S3_ViRGE &&
            pciInfo->ChipType != S3_ViRGE_VX &&
            pciInfo->ChipType != S3_ViRGE_DXGX &&
            pciInfo->ChipType != S3_ViRGE_GX2 &&
            pciInfo->ChipType != S3_ViRGE_MX &&
            pciInfo->ChipType != S3_ViRGE_MXP &&
#if 0 /* not yet */
            pciInfo->ChipType != S3_SAVAGE_3D &&
            pciInfo->ChipType != S3_SAVAGE_3D_M &&
#endif
            pciInfo->ChipType != S3_TRIO_3D &&
            pciInfo->ChipType != S3_TRIO_3D_2X){
            if (xf86Verbose > 1)
                kprintf("%s %s: Unsupported (non-ViRGE) S3 chipset detected!\n", \
                XCONFIG_PROBED, SvgaInfoRec.name);
            return FALSE;
        }
        else {
            s3vPriv.chip = pciInfo->ChipType;
            kprintf("%s %s: Detected S3 %s\n",XCONFIG_PROBED, \
                SvgaInfoRec.name, xf86TokenToString(s3vChipTable, s3vPriv.chip));
            kprintf("%s %s: using driver for chipset \"%s\"\n",XCONFIG_PROBED, \
                SvgaInfoRec.name, S3VIdent(0));
        }
    }

    SvgaInfoRec.chipset = S3VIdent(0);

    /* Add/enable IO ports to list: call EnterLeave */
    S3VEnterLeave(ENTER);

    /* Unlock sys regs */
    Outb(vgaCRIndex, 0x38);
    Outb(vgaCRReg, 0x48);

    /* Next go on to detect amount of installed ram */

    Outb(vgaCRIndex, 0x36);              /* for register CR36 (CONFG_REG1), */
    config1 = Inb(vgaCRReg);              /* get amount of vram installed */

    Outb(vgaCRIndex, 0x37);              /* for register CR37 (CONFG_REG2), */
    config2 = Inb(vgaCRReg);             /* get amount of off-screen ram  */

    Outb(vgaCRIndex, 0x30);
    s3vPriv.ChipId = Inb(vgaCRReg);         /* get chip id */
    Outb(vgaCRIndex, 0x2e);
    s3vPriv.ChipId |= (Inb(vgaCRReg) << 8);

    /* And compute the amount of video memory and offscreen memory */
    s3vPriv.MemOffScreen = 0;
    if (!SvgaInfoRec.videoRam) {
        if (s3vPriv.chip == S3_ViRGE_VX) {
            switch((config2 & 0x60) >> 5) {
            case 1:
                s3vPriv.MemOffScreen = 4 * 1024;
                break;
            case 2:
                s3vPriv.MemOffScreen = 2 * 1024;
                break;
            }
            switch ((config1 & 0x60) >> 5) {
            case 0:
                SvgaInfoRec.videoRam = 2 * 1024;
                break;
            case 1:
                SvgaInfoRec.videoRam = 4 * 1024;
                break;
            case 2:
                SvgaInfoRec.videoRam = 6 * 1024;
                break;
            case 3:
                SvgaInfoRec.videoRam = 8 * 1024;
                break;
            }
            SvgaInfoRec.videoRam -= s3vPriv.MemOffScreen;
        }
        else if (S3_TRIO_3D_2X_SERIES(s3vPriv.chip)) {
            switch((config1 & 0xE0) >> 5) {
            case 0:
                SvgaInfoRec.videoRam = 8 * 1024;
                break;
         case 1:    /* 32 bit interface -- yuck */
         case 2:
             SvgaInfoRec.videoRam = 4 * 1024;
             break;
         case 6:
             SvgaInfoRec.videoRam = 2 * 1024;
             break;
            }
        }
        else if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
            switch((config1 & 0xC0) >> 6) {
            case 1:
                SvgaInfoRec.videoRam = 4 * 1024;
            break;
            case 3:
                SvgaInfoRec.videoRam = 2 * 1024;
                break;
            }
        }
        else if (S3_TRIO_3D_SERIES(s3vPriv.chip)) {
            switch((config1 & 0xE0) >> 5) {
            case 0:
            case 2:
                SvgaInfoRec.videoRam = 4 * 1024;
                break;
            case 4:
                SvgaInfoRec.videoRam = 2 * 1024;
                break;
            }
        }
        else {
            switch((config1 & 0xE0) >> 5) {
            case 0:
                SvgaInfoRec.videoRam = 4 * 1024;
                break;
            case 4:
                SvgaInfoRec.videoRam = 2 * 1024;
                break;
            case 6:
                SvgaInfoRec.videoRam = 1 * 1024;
                break;
            }
        }

        if (xf86Verbose) {
            if (s3vPriv.MemOffScreen)
                kprintf("%s %s: videoram:  %dk (plus %dk off-screen)\n", \
                XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.videoRam, \
                s3vPriv.MemOffScreen);
            else
                kprintf("%s %s: videoram:  %dk\n", \
                XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.videoRam);
        }
    }
    else {
        if (xf86Verbose) {
            kprintf("%s %s: videoram:  %dk\n", \
                XCONFIG_GIVEN, SvgaInfoRec.name, SvgaInfoRec.videoRam);
        }
    }


    /* reset S3 graphics engine to avoid memory corruption */
    if (s3vPriv.chip != S3_ViRGE_VX) {

        /* for virge/DXGX, the next lines will be canceled  dengkang*/
        if (s3vPriv.chip != S3_ViRGE_DXGX) {
            Outb(vgaCRIndex, 0x66);
            cr66 = Inb(vgaCRReg);
            Outb(vgaCRReg, cr66 | 0x02);

            //_CThread_Delay(10000);  /* wait a little bit... */
            DzDelay(1000);
        }
    }

    if (find_bios_string(SvgaInfoRec.BIOSbase,"S3 86C325",
        "MELCO WGP-VG VIDEO BIOS") != NULL) {
        if (xf86Verbose)
            kprintf("%s %s: MELCO BIOS found\n", \
            XCONFIG_PROBED, SvgaInfoRec.name);
        if (SvgaInfoRec.MemClk <= 0)       SvgaInfoRec.MemClk       =  74000;
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 191500;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] = 162500;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] = 111500;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] =  83500;
    }

    if (s3vPriv.chip != S3_ViRGE_VX) {
        /* for virge/DXGX, the next lines will be canceled  dengkang*/
        if (s3vPriv.chip != S3_ViRGE_DXGX) {
            Outb(vgaCRIndex, 0x66);
            Outb(vgaCRReg, cr66 & ~0x02);  /* clear reset flag */

            //_CThread_Delay(10000);  /* wait a little bit... */
            DzDelay(1000);
        }
    }

   /* ViRGE built-in ramdac speeds */

    if (SvgaInfoRec.dacSpeeds[3] <= 0 && SvgaInfoRec.dacSpeeds[2] > 0)
        SvgaInfoRec.dacSpeeds[3] = SvgaInfoRec.dacSpeeds[2];

    if (s3vPriv.chip == S3_ViRGE_VX) {
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 220000;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] = 220000;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] = 135000;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] = 135000;
    }
    else if (s3vPriv.chip == S3_ViRGE_DXGX || S3_ViRGE_GX2_SERIES(s3vPriv.chip)) {
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 170000;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] = 170000;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] = 135000;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] = 135000;
    }
    else if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 135000;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] = 135000;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] = 100000;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] = 100000;
    }
    else if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 230000;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] = 230000;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] = 135000;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] = 135000;
    }
    else {
        if (SvgaInfoRec.dacSpeeds[0] <= 0) SvgaInfoRec.dacSpeeds[0] = 135000;
        if (SvgaInfoRec.dacSpeeds[1] <= 0) SvgaInfoRec.dacSpeeds[1] =  95000;
        if (SvgaInfoRec.dacSpeeds[2] <= 0) SvgaInfoRec.dacSpeeds[2] =  57000;
        if (SvgaInfoRec.dacSpeeds[3] <= 0) SvgaInfoRec.dacSpeeds[3] =  57000;
    }

    if (SvgaInfoRec.dacSpeedBpp <= 0) {
        if (xf86bpp > 24 && SvgaInfoRec.dacSpeeds[3] > 0)
            SvgaInfoRec.dacSpeedBpp = SvgaInfoRec.dacSpeeds[3];
        else if (xf86bpp >= 24 && SvgaInfoRec.dacSpeeds[2] > 0)
            SvgaInfoRec.dacSpeedBpp = SvgaInfoRec.dacSpeeds[2];
        else if (xf86bpp > 8 && xf86bpp < 24 && SvgaInfoRec.dacSpeeds[1] > 0)
            SvgaInfoRec.dacSpeedBpp = SvgaInfoRec.dacSpeeds[1];
        else if (xf86bpp <= 8 && SvgaInfoRec.dacSpeeds[0] > 0)
            SvgaInfoRec.dacSpeedBpp = SvgaInfoRec.dacSpeeds[0];
    }

    if (xf86Verbose) {
        kprintf("%s %s: Ramdac speed: %d MHz", \
            OFLG_ISSET(XCONFIG_DACSPEED, &SvgaInfoRec.xconfigFlag) ?
            XCONFIG_GIVEN : XCONFIG_PROBED, SvgaInfoRec.name,
                SvgaInfoRec.dacSpeeds[0] / 1000);
        if (SvgaInfoRec.dacSpeedBpp != SvgaInfoRec.dacSpeeds[0])
            kprintf("  (%d MHz for %d bpp)",SvgaInfoRec.dacSpeedBpp / 1000, xf86bpp);
        kprintf("\n");
    }


    /* Now set RAMDAC limits */
    SvgaInfoRec.maxClock = SvgaInfoRec.dacSpeedBpp;

    /* Detect current MCLK and print it for user */
    Outb(0x3c4, 0x08);
    sr8 = Inb(0x3c5);
    Outb(0x3c5, 0x06);
    Outb(0x3c4, 0x10);
    n = Inb(0x3c5);
    Outb(0x3c4, 0x11);
    m = Inb(0x3c5);
    Outb(0x3c4, 0x08);
    Outb(0x3c5, sr8);
    m &= 0x7f;
    n1 = n & 0x1f;
    n2 = (n>>5) & 0x03;
    mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
#if 0 //avoid float(2.0 ddk nor sorport float)
    if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        char *is_probed = XCONFIG_PROBED;
        /*
        * try to figure out which reference clock is used:
        * Toshiba Tecra 5x0/7x0 seems to use 28.636 MHz
        * Compaq Armada 7x00 uses 14.318 MHz
        */
        if (find_bios_string(SvgaInfoRec.BIOSbase, "COMPAQ M5 BIOS", NULL) != NULL) {
            if (xf86Verbose)
                kprintf("%s %s: COMPAQ M5 BIOS found\n", XCONFIG_PROBED, SvgaInfoRec.name);
            /* s3vPriv.refclk_fact = 1.0; */
        }
        else if (find_bios_string(SvgaInfoRec.BIOSbase, "TOSHIBA Video BIOS", NULL) != NULL) {
            if (xf86Verbose)
                kprintf("%s %s: TOSHIBA Video BIOS found\n", XCONFIG_PROBED, SvgaInfoRec.name);
            /* s3vPriv.refclk_fact = 2.0; */
        }
        else if (find_bios_string(SvgaInfoRec.BIOSbase, "Generic M5+ BIOS", NULL) != NULL) {
            if (xf86Verbose)
                kprintf("%s %s: Generic M5+ BIOS found\n", XCONFIG_PROBED, SvgaInfoRec.name);
            /* s3vPriv.refclk_fact = 1.0; */
        }
        else if (find_bios_string(SvgaInfoRec.BIOSbase, "M5+ BIOS", NULL) != NULL) {
            if (xf86Verbose)
                kprintf("%s %s: M5+ BIOS found\n", XCONFIG_PROBED, SvgaInfoRec.name);
            /* s3vPriv.refclk_fact = 1.0; */
        }
      /* else */
        {  /* better always use guessed value... */
            if (mclk > 60000)
                s3vPriv.refclk_fact = 1.0;
            else
                s3vPriv.refclk_fact = 2.0;  /* don't know why ??? */
        }
        if (SvgaInfoRec.s3RefClk != 0) {
            s3vPriv.refclk_fact = SvgaInfoRec.s3RefClk / 14318.0;
            is_probed = XCONFIG_GIVEN;
        }
        else
            SvgaInfoRec.s3RefClk = (int)(14318.18 * s3vPriv.refclk_fact);

        mclk = (int)(mclk * s3vPriv.refclk_fact);
        kprintf("%s %s: assuming RefCLK value of %1.3f MHz\n", \
            is_probed, SvgaInfoRec.name, SvgaInfoRec.s3RefClk / 1000.0);
    }
    kprintf("%s %s: Detected current MCLK value of %1.3f MHz\n",XCONFIG_PROBED, \
        SvgaInfoRec.name, mclk / 1000.0);

#endif
   /* Now check if the user has specified "set_memclk" value in XConfig */
    if (SvgaInfoRec.MemClk > 0) {
        if(SvgaInfoRec.MemClk <= 150000) {
/*
            kprintf("%s %s: Using Memory Clock value of %1.3f MHz\n", \
                OFLG_ISSET(XCONFIG_MEMCLOCK, &SvgaInfoRec.xconfigFlag) ? \
                XCONFIG_GIVEN : XCONFIG_PROBED, SvgaInfoRec.name, \
                SvgaInfoRec.MemClk/1000.0);
*/
            s3vPriv.MCLK = SvgaInfoRec.MemClk;

        }
        else {
/*
            kprintf("%s %s: Memory Clock value of %1.3f MHz is larger than limit of 150 MHz\n", \
                XCONFIG_GIVEN, SvgaInfoRec.name, SvgaInfoRec.MemClk/1000.0);
*/
            s3vPriv.MCLK = 0;
        }
    }
    else s3vPriv.MCLK = 0;


    if (S3_ViRGE_MX_SERIES(s3vPriv.chip) && xf86Verbose) {
        int lcdclk, h_lcd, v_lcd;
        if (OFLG_ISSET(XCONFIG_LCDCLOCK, &SvgaInfoRec.xconfigFlag)) {
            lcdclk = SvgaInfoRec.LCDClk;
        }
        else {
            int n1, n2, sr12, sr13, sr29;
            Outb(0x3c4, 0x12);
            sr12 = Inb(0x3c5);
            Outb(0x3c4, 0x13);
            sr13 = Inb(0x3c5) & 0x7f;
            Outb(0x3c4, 0x29);
            sr29 = Inb(0x3c5);
            n1 = sr12 & 0x1f;
            n2 = ((sr12>>6) & 0x03) | ((sr29 & 0x01) << 2);
            lcdclk = ((int)(s3vPriv.refclk_fact * 1431818 * (sr13+2)) / (n1+2) / (1 << n2) + 50) / 100;
        }
        Outb(0x3c4, 0x61);
        h_lcd = Inb(0x3c5);
        Outb(0x3c4, 0x66);
        h_lcd |= ((Inb(0x3c5) & 0x02) << 7);
        h_lcd = (h_lcd+1) * 8;
        Outb(0x3c4, 0x69);
        v_lcd = Inb(0x3c5);
        Outb(0x3c4, 0x6e);
        v_lcd |= ((Inb(0x3c5) & 0x70) << 4);
        v_lcd++;
/*        kprintf("%s %s: LCD size %dx%d, clock %1.3f MHz\n", \
            OFLG_ISSET(XCONFIG_LCDCLOCK, &SvgaInfoRec.xconfigFlag) ? XCONFIG_GIVEN : XCONFIG_PROBED, \
            SvgaInfoRec.name, h_lcd, v_lcd , lcdclk / 1000.0);
*/
    }

    /* And map MMIO memory, abort if we cannot */

    s3vMmioMem = xf86MapVidMem(SvgaInfoRec.scrnIndex, MMIO_REGION,
        (pointer)(SvgaInfoRec.MemBase + S3_NEWMMIO_REGBASE), S3_NEWMMIO_REGSIZE);

    if(s3vMmioMem == NULL)
        kprintf("S3 ViRGE: Cannot map MMIO registers!\n");

    /* Determine if we need to use the STREAMS processor */

    if (vgaBitsPerPixel >= 24) s3vPriv.NeedSTREAMS = TRUE;
    else s3vPriv.NeedSTREAMS = FALSE;
    s3vPriv.STREAMSRunning = FALSE;


    pEnd = pMode = SvgaInfoRec.modes;
    do {
        /* Setup the Mode.Private if required */
        if (!pMode->PrivSize || !pMode->Private) {
            pMode->PrivSize = S3_MODEPRIV_SIZE;
            pMode->Private = (Int32 *)xcalloc(sizeof(Int32), S3_MODEPRIV_SIZE);
            pMode->Private[0] = 0;
        }

        /* Set default for invert_vclk */
        if (!(pMode->Private[0] & (1 << S3_INVERT_VCLK))) {
            pMode->Private[S3_INVERT_VCLK] = 0;
            pMode->Private[0] |= 1 << S3_INVERT_VCLK;
        }

        /* Set default for blank_delay */
        if (!(pMode->Private[0] & (1 << S3_BLANK_DELAY))) {
            pMode->Private[0] |= (1 << S3_BLANK_DELAY);
            if(s3vPriv.chip == S3_ViRGE_VX) {
            /* these values need to be changed once CR67_1 is set
                for gamma correction (see S3V server) ! */
                if (vgaBitsPerPixel == 8) {
                    pMode->Private[S3_BLANK_DELAY] = 0x00;
                }
                else if (vgaBitsPerPixel == 16) {
                    pMode->Private[S3_BLANK_DELAY] = 0x00;
                }
                else {
                    pMode->Private[S3_BLANK_DELAY] = 0x51;
                }
            }
            else {
                if (vgaBitsPerPixel == 8) {
                    pMode->Private[S3_BLANK_DELAY] = 0x00;
                }
                else if (vgaBitsPerPixel == 16) {
                    pMode->Private[S3_BLANK_DELAY] = 0x02;
                }
                else {
                    pMode->Private[S3_BLANK_DELAY] = 0x04;
                }
            }
        }

        /* Set default for early_sc */
        if (!(pMode->Private[0] & (1 << S3_EARLY_SC))) {
            pMode->Private[0] |= 1 << S3_EARLY_SC;
            pMode->Private[S3_EARLY_SC] = 0;
        }
        pMode = pMode->next;
    } while (pMode != pEnd);

    /* And finally set various possible option flags */

    SvgaInfoRec.bankedMono = FALSE;

#ifdef XFreeXDGA
    SvgaInfoRec.directMode = XF86DGADirectPresent;
#endif

    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &SvgaInfoRec.clockOptions);
    OFLG_SET(OPTION_SLOW_EDODRAM, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_FAST_DRAM, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_SLOW_DRAM, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_FPM_VRAM, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_PCI_BURST_ON, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_FIFO_CONSERV, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_FIFO_MODERATE, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_FIFO_AGGRESSIVE, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_PCI_RETRY, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_NOACCEL, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_SW_CURSOR, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_HW_CURSOR, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_EARLY_RAS_PRECHARGE, &S3V.ChipOptionFlags);
    OFLG_SET(OPTION_LATE_RAS_PRECHARGE, &S3V.ChipOptionFlags);
    if (S3_ViRGE_MX_SERIES(s3vPriv.chip))
        OFLG_SET(OPTION_LCD_CENTER, &S3V.ChipOptionFlags);

    s3vPriv.NoPCIRetry = 1;
    S3V.ChipLinearBase = SvgaInfoRec.MemBase;
    S3V.ChipLinearSize = SvgaInfoRec.videoRam * 1024;

    return TRUE;
}


/* This validates a given video mode.
 * Right now, the checks are quite minimal.
 */

static int
S3VValidMode(DisplayModePtr mode, Boolean verbose, int flag)
{
    int mem;

    /* Check horiz/vert total values */

    if(mode->HTotal*s3vPriv.HorizScaleFactor > 4088) {
        if (verbose)
            kprintf("%s %s: %s: Horizontal mode timing overflow (%d)\n", \
            XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.chipset, \
            mode->HTotal);
        return MODE_BAD;
    }
    if (mode->VTotal > 2047) {
        if(verbose)
            kprintf("%s %s: %s: Vertical mode timing overflow (%d)\n", \
            XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.chipset, \
            mode->VTotal);
        return MODE_BAD;
    }
    if((mode->Flags & V_INTERLACE) && (vgaBitsPerPixel >= 24)){
        if(verbose)
            kprintf("%s %s: Interlace modes are not supported at %d bpp\n", \
            XCONFIG_PROBED, SvgaInfoRec.name, vgaBitsPerPixel);
        return MODE_BAD;
    }

    /* For ViRGE and DX, others ?  This limit is imposed by */
    /* the 12 bit Stream stride register.                   */
    if((vgaBitsPerPixel == 32) && (mode->HDisplay > 1023) &&
        ((s3vPriv.chip == S3_ViRGE) || (s3vPriv.chip == S3_ViRGE_DXGX))
        ) {
        if(verbose)
            kprintf("%s %s: %s: Mode '%s' discarded. Width (%d) > 1023 at 32 bpp.\n", \
            XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.chipset,
            mode->name, mode->HDisplay);
        return MODE_BAD;
    }

    /* Now make sure we have enough vidram to support mode */
    mem = ((SvgaInfoRec.displayWidth > mode->HDisplay) ?
        SvgaInfoRec.displayWidth : mode->HDisplay)
        * (SvgaInfoRec.bitsPerPixel / 8) *
        SvgaInfoRec.virtualY;
    if (mem > (1024 * SvgaInfoRec.videoRam - 1024)) {
        kprintf("%s %s: Mode \"%s\" requires %d of videoram, only %d is available\n", \
            XCONFIG_PROBED, SvgaInfoRec.name, mode->name, mem,
            1024 * SvgaInfoRec.videoRam - 1024);
        return MODE_BAD;
    }

    /* Dont check anything else for now. Leave the warning, fix it later. */

    return MODE_OK;
}


/* Used to adjust start address in frame buffer. We use the new
 * CR69 reg for this purpose instead of the older CR31/CR51 combo.
 * If STREAMS is running, we program the STREAMS start addr. registers.
 */

static void
S3VAdjust(int x, int y)
{
    int Base;//, hwidth;
//    unsigned char tmp;

    if(s3vPriv.STREAMSRunning == FALSE ||
        S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        Base = ((y * SvgaInfoRec.displayWidth + x)
            * (vgaBitsPerPixel / 8)) >> 2;
        if (vgaBitsPerPixel == 24)
            Base = Base+2 - (Base+2) % 3;
        if (vgaBitsPerPixel == 16)
            if (S3_TRIO_3D_SERIES(s3vPriv.chip)
                && SvgaInfoRec.clock[SvgaInfoRec.modes->Clock] > 115000)
                Base &= ~1;

            /* Now program the start address registers */
            Outw(vgaCRIndex, (Base & 0x00FF00) | 0x0C);
            Outw(vgaCRIndex, ((Base & 0x00FF) << 8) | 0x0D);
            Outb(vgaCRIndex, 0x69);
            Outb(vgaCRReg, (Base & 0x0F0000) >> 16);
    }
    else {          /* Change start address for STREAMS case */
        VerticalRetraceWait();
        if(s3vPriv.chip == S3_ViRGE_VX)
            ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr0 =
            ((y * SvgaInfoRec.displayWidth + (x & ~7)) * vgaBitsPerPixel / 8);
        else
            ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr0 =
            ((y * SvgaInfoRec.displayWidth + (x & ~3)) * vgaBitsPerPixel / 8);
    }

#if 0
    if (!OFLG_ISSET(OPTION_SW_CURSOR, &SvgaInfoRec.options))
        S3VRepositionCursor(NULL);
#endif

#ifdef XFreeXDGA
    if (SvgaInfoRec.directMode & XF86DGADirectGraphics) {
        /* Wait until vertical retrace is in progress. */
        VerticalRetraceWait();
    }
#endif

    return;
}


static Boolean
S3VInit(DisplayModePtr mode)
{
#if 0 //avoid float(2.0 ddk nor sorport float)
    unsigned char tmp;
    int width,dclk;
    int i, j;

    if (xf86Verbose > 1) {
        kprintf("switching to Mode %s\n",mode->name);
    }


    /* Set scale factors for mode timings */
    if (s3vPriv.chip == S3_ViRGE_VX || S3_ViRGE_GX2_SERIES(s3vPriv.chip) ||
        S3_ViRGE_MX_SERIES(s3vPriv.chip)){
        s3vPriv.HorizScaleFactor = 1;
    }
    else if (vgaBitsPerPixel == 8){
        s3vPriv.HorizScaleFactor = 1;
    }
    else if (vgaBitsPerPixel == 16){
        if(S3_TRIO_3D_SERIES(s3vPriv.chip)
            && SvgaInfoRec.clock[SvgaInfoRec.modes->Clock] > 115000)
            s3vPriv.HorizScaleFactor = 1;
        else
            s3vPriv.HorizScaleFactor = 2;
    }
    else {
        s3vPriv.HorizScaleFactor = 1;
    }


   /* First we adjust the horizontal timings if needed */

    if(s3vPriv.HorizScaleFactor != 1) {
        if (!mode->CrtcHAdjusted) {
            mode->CrtcHDisplay *= s3vPriv.HorizScaleFactor;
            mode->CrtcHSyncStart *= s3vPriv.HorizScaleFactor;
            mode->CrtcHSyncEnd *= s3vPriv.HorizScaleFactor;
            mode->CrtcHTotal *= s3vPriv.HorizScaleFactor;
            mode->CrtcHSkew *= s3vPriv.HorizScaleFactor;
            mode->CrtcHAdjusted = TRUE;
        }
    }



    if(!vgaHWInit (mode, sizeof(vgaS3VRec)))
        return FALSE;

    /* Now we fill in the rest of the stuff we need for the virge */
    /* Start with MMIO, linear addr. regs */

    Outb(vgaCRIndex, 0x3a);
    tmp = Inb(vgaCRReg);
    if(!OFLG_ISSET(OPTION_PCI_BURST_ON, &SvgaInfoRec.options))
        new->CR3A = tmp | 0x95;      /* ENH 256, no PCI burst! */
    else
        new->CR3A = (tmp & 0x7f) | 0x15; /* ENH 256, PCI burst */

    if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        new->CR31 = 0x0c;               /* [trio3d] page 54 */
    } else {
        new->CR53 = 0x08;     /* Enables MMIO */
        new->CR31 = 0x8c;     /* Dis. 64k window, en. ENH maps */
    }

    /* Enables S3D graphic engine and PCI disconnects */
    if(s3vPriv.chip == S3_ViRGE_VX){
        new->CR66 = 0x90;
        new->CR63 = 0x09;
    }
    else if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        new->CR66 = 0x89;
    } else {
        new->CR66 = 0x89;
        new->CR63 = 0;
    }

    /* Now set linear addr. registers */

    Outb(vgaCRIndex, 0x58);
    new->CR58 = Inb(vgaCRReg) & 0x80;
    if(SvgaInfoRec.videoRam == 2048) {
        new->CR58 |= 0x02 | 0x10;
    }
    else if(SvgaInfoRec.videoRam == 1024) {
        new->CR58 |= 0x01 | 0x10;
    }
    else {
        if (S3_TRIO_3D_2X_SERIES(s3vPriv.chip) && SvgaInfoRec.videoRam == 8192)
            new->CR58 |= 0x07 | 0x10; /* 8MB window on Trio3D/2X */
        else
            new->CR58 |= 0x03 | 0x10; /* 4MB window on virge, 8MB on VX */
    }
    if(s3vPriv.chip == S3_ViRGE_VX)
        new->CR58 |= 0x40;
    if (OFLG_ISSET(OPTION_EARLY_RAS_PRECHARGE, &SvgaInfoRec.options))
        new->CR58 |= 0x80;
    if (OFLG_ISSET(OPTION_LATE_RAS_PRECHARGE, &SvgaInfoRec.options))
        new->CR58 &= 0x7f;

    /* ** On PCI bus, no need to reprogram the linear window base address */

    /* Now do clock PLL programming. Use the s3gendac function to get m,n */
    /* Also determine if we need doubling etc. */

    dclk = SvgaInfoRec.clock[mode->Clock];
    new->CR67 = 0x00;             /* Defaults */

    if (!S3_TRIO_3D_SERIES(s3vPriv.chip))
        new->SR15 = 0x03 | 0x80;
    else {
        Outb(0x3c4, 0x15);
        new->SR15 = Inb(0x3c5);
        Outb(0x3c4, 0x0a);
        new->SR0A = Inb(0x3c5);
        if (OFLG_ISSET(OPTION_SLOW_DRAM, &SvgaInfoRec.options)) {
            new->SR15 = 0x03;  /* 3 CYC MWR */
            new->SR0A &= 0x7F;
        } else if (OFLG_ISSET(OPTION_FAST_DRAM, &SvgaInfoRec.options)) {
            new->SR15 = 0x03 | 0x80; /* 2 CYC MWR */
            new->SR0A |= 0x80;
        } else { /* keep BIOS init defaults */
            new->SR15 = (new->SR15 & 0x80) | 0x03;
        }
    }
    new->SR18 = 0x00;
    new->CR43 = 0x00;
    new->CR45 = 0x00;
    new->CR65 = 0x00;
    new->CR54 = 0x00;
    new->CR55 = 0x00;

    if (!S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        Outb(vgaCRIndex, 0x40);
        new->CR40 = Inb(vgaCRReg) & ~0x01;
    }

    if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        /* fix problems with APM suspend/resume trashing CR90/91 */
        switch(SvgaInfoRec.bitsPerPixel) {
        case  8: new->CR41 = 0x38; break;
        case 15: new->CR41 = 0x58; break;
        case 16: new->CR41 = 0x48; break;
        default: new->CR41 = 0x77;
        }
    }

    /* Memory controller registers. Optimize for better graphics engine
    * performance. These settings are adjusted/overridden below for other bpp/
    * XConfig options.The idea here is to give a longer number of contiguous
    * MCLK's to both refresh and the graphics engine, to diminish the
    * relative penalty of 3 or 4 mclk's needed to setup memory transfers.
    */
    new->MMPR0 = 0x010400; /* defaults */
    new->MMPR1 = 0x00;
    new->MMPR2 = 0x0808;
    new->MMPR3 = 0x08080810;


    if (OFLG_ISSET(OPTION_FIFO_AGGRESSIVE, &SvgaInfoRec.options) ||
        OFLG_ISSET(OPTION_FIFO_MODERATE, &SvgaInfoRec.options) ||
        OFLG_ISSET(OPTION_FIFO_CONSERV, &SvgaInfoRec.options)) {
        new->MMPR1 = 0x0200;   /* Low P. stream waits before filling */
        new->MMPR2 = 0x1808;   /* Let the FIFO refill itself */
        new->MMPR3 = 0x08081810; /* And let the GE hold the bus for a while */
    }

   /* And setup here the new value for MCLK. We use the XConfig
    * option "set_mclk", whose value gets stored in SvgaInfoRec.s3MClk.
    * I'm not sure what the maximum "permitted" value should be, probably
    * 100 MHz is more than enough for now.
    */

    if(s3vPriv.MCLK> 0) {
        if (S3_ViRGE_MX_SERIES(s3vPriv.chip))
            commonCalcClock((int)(s3vPriv.MCLK/s3vPriv.refclk_fact), 1, 1, 31, 0, 3,
            135000, 270000, &new->SR11, &new->SR10);
        else
            commonCalcClock(s3vPriv.MCLK, 1, 1, 31, 0, 3,
            135000, 270000, &new->SR11, &new->SR10);
    }
    else {
        new->SR10 = 255; /* This is a reserved value, so we use as flag */
        new->SR11 = 255;
    }

    if(s3vPriv.chip == S3_ViRGE_VX){
        if (vgaBitsPerPixel == 8) {
            if (dclk <= 110000) new->CR67 = 0x00; /* 8bpp, 135MHz */
            else new->CR67 = 0x10;                /* 8bpp, 220MHz */
        }
        else if ((vgaBitsPerPixel == 16) && (SvgaInfoRec.weight.green == 5)) {
            if (dclk <= 110000) new->CR67 = 0x20; /* 15bpp, 135MHz */
            else new->CR67 = 0x30;                /* 15bpp, 220MHz */
        }
        else if (vgaBitsPerPixel == 16) {
            if (dclk <= 110000) new->CR67 = 0x40; /* 16bpp, 135MHz */
            else new->CR67 = 0x50;                /* 16bpp, 220MHz */
        }
        else if ((vgaBitsPerPixel == 24) || (vgaBitsPerPixel == 32)) {
            new->CR67 = 0xd0 | 0x0c;              /* 24bpp, 135MHz, STREAMS */
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0xc098;            /* Adjust FIFO slots */
        }
        commonCalcClock(dclk, 1, 1, 31, 0, 4,
            220000, 440000, &new->SR13, &new->SR12);
    }
    else if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) ||
        S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        if (vgaBitsPerPixel == 8)
            new->CR67 = 0x00;
        else if (vgaBitsPerPixel == 16) {
            if (SvgaInfoRec.weight.green == 5)
                new->CR67 = 0x30;                  /* 15bpp */
            else
                new->CR67 = 0x50;                  /* 16bpp */
        }
        else if ((vgaBitsPerPixel == 24) /* || (vgaBitsPerPixel == 32) */ ) {
            new->CR67 = 0x74;              /* 24bpp, STREAMS */
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0xc098;            /* Adjust FIFO slots */
        }
        else if (vgaBitsPerPixel == 32) {
            new->CR67 = 0xd0;              /* 32bpp */
            // new->MMPR0 = 0xc098;            /* Adjust FIFO slots */
        }
        {
            unsigned char ndiv;
            if (S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
//                int lcd_freq;
                unsigned char sr8;
                Outb(0x3c4, 0x08);  /* unlock extended SEQ regs */
                sr8 = Inb(0x3c5);
                Outb(0x3c5, 0x06);
                Outb(0x3c4, 0x31);
                if (Inb(0x3c5) & 0x10) { /* LCD on */
                    if (!SvgaInfoRec.LCDClk) {  /* entered only once for first mode */
                        int h_lcd, v_lcd;
                        Outb(0x3c4, 0x61);
                        h_lcd = Inb(0x3c5);
                        Outb(0x3c4, 0x66);
                        h_lcd |= ((Inb(0x3c5) & 0x02) << 7);
                        h_lcd = (h_lcd+1) * 8;
                        Outb(0x3c4, 0x69);
                        v_lcd = Inb(0x3c5);
                        Outb(0x3c4, 0x6e);
                        v_lcd |= ((Inb(0x3c5) & 0x70) << 4);
                        v_lcd++;

                        /* check if first mode has physical LCD resolution */
                        if (SvgaInfoRec.modes->HDisplay == h_lcd && SvgaInfoRec.modes->VDisplay == v_lcd)
                            SvgaInfoRec.LCDClk = SvgaInfoRec.clock[SvgaInfoRec.modes->Clock];
                        else {
                            int n1, n2, sr12, sr13, sr29;
                            Outb(0x3c4, 0x12);
                            sr12 = Inb(0x3c5);
                            Outb(0x3c4, 0x13);
                            sr13 = Inb(0x3c5) & 0x7f;
                            Outb(0x3c4, 0x29);
                            sr29 = Inb(0x3c5);
                            n1 = sr12 & 0x1f;
                            n2 = ((sr12>>6) & 0x03) | ((sr29 & 0x01) << 2);
                            SvgaInfoRec.LCDClk = ((int)(s3vPriv.refclk_fact * 1431818 * (sr13+2)) / (n1+2) / (1 << n2) + 50) / 100;
                        }
                    }
                    commonCalcClock((int)(SvgaInfoRec.LCDClk / s3vPriv.refclk_fact),
                        1, 1, 31, 0, 4,
                        170000, 340000, &new->SR13, &ndiv);
                }
                else
                    commonCalcClock((int)(dclk / s3vPriv.refclk_fact), 1, 1, 31, 0, 4,
                    170000, 340000, &new->SR13, &ndiv);
                Outb(0x3c4, 0x08);
                Outb(0x3c5, sr8);
            }
            else  /* S3_ViRGE_GX2 */
                commonCalcClock(dclk, 1, 1, 31, 0, 4,
                170000, 340000, &new->SR13, &ndiv);
            new->SR29 = ndiv >> 7;
            new->SR12 = (ndiv & 0x1f) | ((ndiv & 0x60) << 1);
        }
    }
    else if(S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        new->SR0F = 0x00;
        if (vgaBitsPerPixel == 8) {
            if(dclk > 115000) {                     /* We need pixmux */
                new->CR67 = 0x10;
                new->SR15 |= 0x10;                   /* Set DCLK/2 bit */
                new->SR18 = 0x80;                   /* Enable pixmux */
            }
        }
        else if ((vgaBitsPerPixel == 16) && (SvgaInfoRec.weight.green == 5)) {
            if(dclk > 115000) {
                new->CR67 = 0x20;
                new->SR15 |= 0x10;
                new->SR18 = 0x80;
                new->SR0F = 0x10;
            } else {
                new->CR67 = 0x30;                       /* 15bpp */
            }
        }
        else if (vgaBitsPerPixel == 16) {
            if(dclk > 115000) {
                new->CR67 = 0x40;
                new->SR15 |= 0x10;
                new->SR18 = 0x80;
                new->SR0F = 0x10;
            } else {
                new->CR67 = 0x50;
            }
        }
        else if (vgaBitsPerPixel == 24) {
            new->CR67 = 0xd0 | 0x0c;
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0xc000;            /* Adjust FIFO slots */
        }
        else if (vgaBitsPerPixel == 32) {
            new->CR67 = 0xd0 | 0x0c;
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0x10000;            /* Still more FIFO slots */
            new->SR0F = 0x10;
        }
        commonCalcClock(dclk, 1, 1, 31, 0, 4,
            230000, 460000, &new->SR13, &new->SR12);
    }
    else {           /* Is this correct for DX/GX as well? */
        if (vgaBitsPerPixel == 8) {
            if(dclk > 80000) {                     /* We need pixmux */
                new->CR67 = 0x10;
                new->SR15 |= 0x10;                   /* Set DCLK/2 bit */
                new->SR18 = 0x80;                   /* Enable pixmux */
            }
        }
        else if ((vgaBitsPerPixel == 16) && (SvgaInfoRec.weight.green == 5)) {
            new->CR67 = 0x30;                       /* 15bpp */
        }
        else if (vgaBitsPerPixel == 16) {
            new->CR67 = 0x50;
        }
        else if (vgaBitsPerPixel == 24) {
            new->CR67 = 0xd0 | 0x0c;
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0xc000;            /* Adjust FIFO slots */
        }
        else if (vgaBitsPerPixel == 32) {
            new->CR67 = 0xd0 | 0x0c;
            S3VInitSTREAMS((int*)new->STREAMS, mode);
            new->MMPR0 = 0x10000;            /* Still more FIFO slots */
        }
        commonCalcClock(dclk, 1, 1, 31, 0, 3,
            135000, 270000, &new->SR13, &new->SR12);
    }

    /* Now adjust the value of the FIFO based upon options specified */
    if(OFLG_ISSET(OPTION_FIFO_MODERATE, &SvgaInfoRec.options)) {
        if(vgaBitsPerPixel < 24)
            new->MMPR0 -= 0x8000;
        else
            new->MMPR0 -= 0x4000;
    }
    else if(OFLG_ISSET(OPTION_FIFO_AGGRESSIVE, &SvgaInfoRec.options)) {
        if(vgaBitsPerPixel < 24)
            new->MMPR0 -= 0xc000;
        else
            new->MMPR0 -= 0x6000;
    }

    /* If we have an interlace mode, set the interlace bit. Note that mode
    * vertical timings are already adjusted by the standard VGA code
    */
    if(mode->Flags & V_INTERLACE) {
        new->CR42 = 0x20; /* Set interlace mode */
    }
    else {
        new->CR42 = 0x00;
    }

    /* Set display fifo */
    new->CR34 = 0x10;

    /* Now we adjust registers for extended mode timings */
    /* This is taken without change from the accel/s3_virge code */

    i = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8) |
        ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) |
        ((((mode->CrtcHSyncStart >> 3) - 1) & 0x100) >> 6) |
        ((mode->CrtcHSyncStart & 0x800) >> 7);

    if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 64)
        i |= 0x08;   /* add another 64 DCLKs to blank pulse width */

    if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 32)
        i |= 0x20;   /* add another 32 DCLKs to hsync pulse width */

    j = (  new->std.CRTC[0] + ((i&0x01)<<8)
        + new->std.CRTC[4] + ((i&0x10)<<4) + 1) / 2;

    if (j-(new->std.CRTC[4] + ((i&0x10)<<4)) < 4) {
        if (new->std.CRTC[4] + ((i&0x10)<<4) + 4 <= new->std.CRTC[0]+ ((i&0x01)<<8))
            j = new->std.CRTC[4] + ((i&0x10)<<4) + 4;
        else
            j = new->std.CRTC[0]+ ((i&0x01)<<8) + 1;
    }

    new->CR3B = j & 0xFF;
    i |= (j & 0x100) >> 2;
    new->CR3C = (new->std.CRTC[0] + ((i&0x01)<<8))/2;
    new->CR5D = i;

    new->CR5E = (((mode->CrtcVTotal - 2) & 0x400) >> 10)  |
        (((mode->CrtcVDisplay - 1) & 0x400) >> 9) |
        (((mode->CrtcVSyncStart) & 0x400) >> 8)   |
        (((mode->CrtcVSyncStart) & 0x400) >> 6)   | 0x40;


    width = (SvgaInfoRec.displayWidth * (vgaBitsPerPixel / 8))>> 3;
    new->std.CRTC[19] = 0xFF & width;
    new->CR51 = (0x300 & width) >> 4; /* Extension bits */

    /* And finally, select clock source 2 for programmable PLL */
    new->std.MiscOutReg |= 0x0c;


    new->CR33 = 0x20;
    if ((s3vPriv.chip == S3_ViRGE_DXGX) || (S3_TRIO_3D_SERIES(s3vPriv.chip))) {
        new->CR86 = 0x80;  /* disable DAC power saving to avoid bright left edge */
    }
    if (s3vPriv.chip == S3_ViRGE_DXGX || S3_ViRGE_GX2_SERIES(s3vPriv.chip) ||
        S3_ViRGE_MX_SERIES(s3vPriv.chip) || S3_TRIO_3D_SERIES(s3vPriv.chip)) {
        int dbytes = SvgaInfoRec.displayWidth * ((SvgaInfoRec.bitsPerPixel+7)/8);
        new->CR91 =   (dbytes + 7) / 8;
        new->CR90 = (((dbytes + 7) / 8) >> 8) | 0x80;
    }


    /* Now we handle various XConfig memory options and others */


    Outb(vgaCRIndex, 0x36);
    new->CR36 = Inb(vgaCRReg);
    /* option "slow_edodram" sets EDO to 2 cycle mode on ViRGE */
    if (s3vPriv.chip == S3_ViRGE) {
        if(OFLG_ISSET(OPTION_SLOW_EDODRAM, &SvgaInfoRec.options))
            new->CR36 = (new->CR36 & 0xf3) | 0x08;
        else
            new->CR36 &= 0xf3;
    }

    /* Option "fpm_vram" for ViRGE_VX sets memory in fast page mode */
    if (s3vPriv.chip == S3_ViRGE_VX) {
        if(OFLG_ISSET(OPTION_FPM_VRAM, &SvgaInfoRec.options))
            new->CR36 |=  0x0c;
        else
            new->CR36 &= ~0x0c;
    }

    if (mode->Private) {
        if (mode->Private[0] & (1 << S3_INVERT_VCLK)) {
            if (mode->Private[S3_INVERT_VCLK])
                new->CR67 |= 1;
            else
                new->CR67 &= ~1;
        }
        if (mode->Private[0] & (1 << S3_BLANK_DELAY)) {
            if (s3vPriv.chip == S3_ViRGE_VX)
                new->CR6D = mode->Private[S3_BLANK_DELAY];
            else {
                new->CR65 = (new->CR65 & ~0x38)
                    | (mode->Private[S3_BLANK_DELAY] & 0x07) << 3;
                Outb(vgaCRIndex, 0x6d);
                new->CR6D = Inb(vgaCRReg);
            }
        }
        if (mode->Private[0] & (1 << S3_EARLY_SC)) {
            if (mode->Private[S3_EARLY_SC])
                new->CR65 |= 2;
            else
                new->CR65 &= ~2;
        }
    }
    else {
        Outb(vgaCRIndex, 0x6d);
        new->CR6D = Inb(vgaCRReg);
    }

    Outb(vgaCRIndex, 0x68);
    new->CR68 = Inb(vgaCRReg);
    new->CR69 = 0;

    if (S3_ViRGE_GX2_SERIES(s3vPriv.chip) || S3_ViRGE_MX_SERIES(s3vPriv.chip)) {
        if (OFLG_ISSET(OPTION_LCD_CENTER, &SvgaInfoRec.options)) {
            new->SR54 = 0x10 ;
            new->SR55 = 0x80 ;
            new->SR56 = 0x10 ;
            new->SR57 = 0x80 ;
        } else {
            new->SR54 = 0x1f ;
            new->SR55 = 0x9f ;
            new->SR56 = 0x1f ;
            new->SR57 = 0xff ;
        }
    }
#endif
    return TRUE;
}

/* This function inits the frame buffer. Right now, it is is rather limited
 * but the hardware cursor hooks should probably end up here
 */

void
S3VFbInit()
{
    /* Call S3VAccelInit to setup the XAA accelerated functions */
    if(!OFLG_ISSET(OPTION_NOACCEL, &SvgaInfoRec.options)) {
#if 0
        if(vgaBitsPerPixel == 32)
            S3VAccelInit32();
        else
            S3VAccelInit();
#endif
    }

    if(OFLG_ISSET(OPTION_PCI_RETRY, &SvgaInfoRec.options)) {
        if(OFLG_ISSET(OPTION_PCI_BURST_ON, &SvgaInfoRec.options)) {
            s3vPriv.NoPCIRetry = 0;
        }
        else {
            s3vPriv.NoPCIRetry = 1;
            kprintf("%s %s: \"pci_retry\" option requires \"pci_burst\".\n", \
                XCONFIG_GIVEN, SvgaInfoRec.name);
        }
    }
    if (!OFLG_ISSET(OPTION_SW_CURSOR, &SvgaInfoRec.options)) {
#if 0
        vgaHWCursor.Initialized = TRUE;
        vgaHWCursor.Init = S3VCursorInit;
        vgaHWCursor.Restore = S3VRestoreCursor;
        vgaHWCursor.Warp = S3VWarpCursor;
        vgaHWCursor.QueryBestSize = S3VQueryBestSize;
#endif
        if (xf86Verbose) {
            kprintf("%s %s: %s: Using hardware cursor\n", \
                XCONFIG_PROBED, SvgaInfoRec.name, SvgaInfoRec.chipset);
        }
    }
}


/* This function inits the STREAMS processor variables.
 * This has essentially been taken from the accel/s3_virge code and the databook.
 */
void
S3VInitSTREAMS(int * streams, DisplayModePtr mode)
{

    if ( SvgaInfoRec.bitsPerPixel == 24 ) {
        /* data format 8.8.8 (24 bpp) */
        streams[0] = 0x06000000;
    }
    else if (SvgaInfoRec.bitsPerPixel == 32) {
        /* one more bit for X.8.8.8, 32 bpp */
        streams[0] = 0x07000000;
    }
    /* NO chroma keying... */
    streams[1] = 0x0;
    /* Secondary stream format KRGB-16 */
    /* data book suggestion... */
    streams[2] = 0x03000000;

    streams[3] = 0x0;

    streams[4] = 0x0;
    /* use 0x01000000 for primary over second. */
    /* use 0x0 for second over prim. */
    streams[5] = 0x01000000;

    streams[6] = 0x0;

    streams[7] = 0x0;
    /* Stride is 3 bytes for 24 bpp mode and */
    /* 4 bytes for 32 bpp. */
    if ( SvgaInfoRec.bitsPerPixel == 24 ) {
        streams[8] =
            SvgaInfoRec.displayWidth * 3;
    }
    else {
        streams[8] =
            SvgaInfoRec.displayWidth * 4;
    }
    /* Choose fbaddr0 as stream source. */
    streams[9] = 0x0;
    streams[10] = 0x0;
    streams[11] = 0x0;
    streams[12] = 0x1;

    /* Set primary stream on top of secondary */
    /* stream. */
    streams[13] = 0xc0000000;
    /* Vertical scale factor. */
    streams[14] = 0x0;

    streams[15] = 0x0;
    /* Vertical accum. initial value. */
    streams[16] = 0x0;
    /* X and Y start coords + 1. */
    streams[18] =  0x00010001;

    /* Specify window Width -1 and Height of */
    /* stream. */
    streams[19] =
        (mode->HDisplay - 1) << 16 |
        (mode->VDisplay);

    /* Book says 0x07ff07ff. */
    streams[20] = 0x07ff07ff;

    streams[21] = 0x00010001;
}

/* This function saves the STREAMS registers to our private structure */
void
S3VSaveSTREAMS(int *streams)
{
    streams[0] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_stream_cntl;
    streams[1] = ((mmtr)s3vMmioMem)->streams_regs.regs.col_chroma_key_cntl;
    streams[2] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_cntl;
    streams[3] = ((mmtr)s3vMmioMem)->streams_regs.regs.chroma_key_upper_bound;
    streams[4] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_stretch;
    streams[5] = ((mmtr)s3vMmioMem)->streams_regs.regs.blend_cntl;
    streams[6] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr0;
    streams[7] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr1;
    streams[8] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_stream_stride;
    streams[9] = ((mmtr)s3vMmioMem)->streams_regs.regs.double_buffer;
    streams[10] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_fbaddr0;
    streams[11] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_fbaddr1;
    streams[12] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_stride;
    streams[13] = ((mmtr)s3vMmioMem)->streams_regs.regs.opaq_overlay_cntl;
    streams[14] = ((mmtr)s3vMmioMem)->streams_regs.regs.k1;
    streams[15] = ((mmtr)s3vMmioMem)->streams_regs.regs.k2;
    streams[16] = ((mmtr)s3vMmioMem)->streams_regs.regs.dda_vert;
    streams[17] = ((mmtr)s3vMmioMem)->streams_regs.regs.streams_fifo;
    streams[18] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_start_coord;
    streams[19] = ((mmtr)s3vMmioMem)->streams_regs.regs.prim_window_size;
    streams[20] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_start_coord;
    streams[21] = ((mmtr)s3vMmioMem)->streams_regs.regs.second_window_size;
}

/* This function restores the saved STREAMS registers */

void
S3VRestoreSTREAMS(int *streams)
{
    /* For now, set most regs to their default values for 24bpp
     * Restore only those that are needed for width/height/stride
     * Otherwise, we seem to get lockups because some registers
     * when saved have some reserved bits set.
     */

    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_stream_cntl =
        streams[0] & 0x77000000;
    ((mmtr)s3vMmioMem)->streams_regs.regs.col_chroma_key_cntl = streams[1];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_cntl = streams[2];
    ((mmtr)s3vMmioMem)->streams_regs.regs.chroma_key_upper_bound = streams[3];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_stretch = streams[4];
    ((mmtr)s3vMmioMem)->streams_regs.regs.blend_cntl = streams[5];
    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr0 = streams[6];
    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_fbaddr1 = streams[7];
    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_stream_stride =
        streams[8] & 0x0fff;
    ((mmtr)s3vMmioMem)->streams_regs.regs.double_buffer = streams[9];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_fbaddr0 = streams[10];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_fbaddr1 = streams[11];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_stream_stride = streams[12];
    ((mmtr)s3vMmioMem)->streams_regs.regs.opaq_overlay_cntl = streams[13];
    ((mmtr)s3vMmioMem)->streams_regs.regs.k1 = streams[14];
    ((mmtr)s3vMmioMem)->streams_regs.regs.k2 = streams[15];
    ((mmtr)s3vMmioMem)->streams_regs.regs.dda_vert = streams[16];
    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_start_coord = streams[18];
    ((mmtr)s3vMmioMem)->streams_regs.regs.prim_window_size =
        streams[19] & 0x07ff07ff;
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_start_coord = streams[20];
    ((mmtr)s3vMmioMem)->streams_regs.regs.second_window_size = streams[21];
}


/* And this function disables the STREAMS processor as per databook.
 * This is usefull before we do a mode change
 */

void
S3VDisableSTREAMS()
{
    unsigned char tmp;

    VerticalRetraceWait();
    ((mmtr)s3vMmioMem)->memport_regs.regs.fifo_control = 0xC000;
    Outb(vgaCRIndex, 0x67);
    tmp = Inb(vgaCRReg);
    /* Disable STREAMS processor */
    Outb( vgaCRReg, tmp & ~0x0C );
}

/* This function is used to debug, it prints out the contents of s3 regs */

void
S3VPrintRegs(void)
{
    unsigned char tmp1, tmp2, lock;

    /* First unlock extended sequencer regs */
    Outb(0x3c4, 0x08);
    lock = Inb(0x3c5);
    Outb(0x3c5, 0x06);

    Outb(0x3c4, 0x10);
    tmp1 = Inb(0x3c5);
    Outb(0x3c4, 0x11);
    tmp2 = Inb(0x3c5);
    kprintf("SR10: %02x SR11: %02x\n", tmp1, tmp2);

    Outb(0x3c4, 0x12);
    tmp1 = Inb(0x3c5);
    Outb(0x3c4, 0x13);
    tmp2 = Inb(0x3c5);
    kprintf("SR12: %02x SR13: %02x\n", tmp1, tmp2);

    Outb(0x3c4, 0x0a);
    tmp1 = Inb(0x3c5);
    Outb(0x3c4, 0x15);
    tmp2 = Inb(0x3c5);
    kprintf("SR0A: %02x SR15: %02x\n", tmp1, tmp2);

    /* lock them again */
    Outb(0x3c4, 0x08);
    Outb(0x3c5, lock);

    /* now unlock the crtc regs */
    Outb(0x3c4, 0x39);
    lock = Inb(0x3c5);
    Outb(0x3c5, 0xa0);


    /* Now load and print a whole rnage of other regs */
    for(tmp1=0x0;tmp1<=0x6f;tmp1++){
        Outb(vgaCRIndex, tmp1);
        kprintf("CR%02x:%02x ",tmp1,Inb(vgaCRReg));
        if(!(tmp1 & 7))
            kprintf("\n");
    }
    /* lock them again */
    Outb(0x3c4, 0x39);
    Outb(0x3c5, lock);

    kprintf("\n\n");
}
